package com.example.rabbitmq.starter.producer;

import com.example.rabbitmq.starter.entity.MQConfigInfo;
import com.example.rabbitmq.starter.model.MessagePayload;
import com.example.rabbitmq.starter.service.MQConfigService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.MessagePropertiesBuilder;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;

import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 消息生产者工厂，用于创建消息生产者
 */
@Component
@RequiredArgsConstructor
public class MessageProducerFactory {

    private static final Logger log = LoggerFactory.getLogger(MessageProducerFactory.class);

    private final RabbitTemplate rabbitTemplate;

    private final ObjectMapper objectMapper;

    private final MQConfigService mqConfigService;

    /**
     * 根据消息类型获取相应的消息生产者
     *
     * @param messageType 消息类型
     * @return 消息生产者
     */
    public BusinessMessageProducer getProducer(String messageType) {
        MQConfigInfo configInfo = mqConfigService.getConfigByMessageType(messageType);
        if (configInfo == null) {
            throw new IllegalArgumentException("找不到消息类型对应的配置: " + messageType);
        }
        
        if (!configInfo.isEnabled()) {
            throw new IllegalArgumentException("该消息类型的配置已禁用: " + messageType);
        }
        
        return new BusinessMessageProducer(configInfo);
    }

    /**
     * 业务消息生产者，根据MQ配置信息发送消息
     */
    public class BusinessMessageProducer {
        private final MQConfigInfo configInfo;

        public BusinessMessageProducer(MQConfigInfo configInfo) {
            this.configInfo = configInfo;
        }

        /**
         * 发送业务消息（使用配置的默认路由键）
         * 适用于Direct交换机或有固定路由规则的场景
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @return 是否发送成功
         */
        public boolean sendMessage(String businessId, Map<String, Object> content) {
            return sendMessageWithRoutingKey(businessId, content, configInfo.getRoutingKey());
        }
        
        /**
         * 发送业务消息（指定路由键）
         * 适用于Topic交换机等需要动态路由的场景
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @param routingKey 路由键
         * @return 是否发送成功
         */
        public boolean sendMessage(String businessId, Map<String, Object> content, String routingKey) {
            return sendMessageWithRoutingKey(businessId, content, routingKey);
        }
        
        /**
         * 发送业务消息（带优先级，使用配置的默认路由键）
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @param priority   优先级（0-255，数字越大优先级越高）
         * @return 是否发送成功
         */
        public boolean sendMessageWithPriority(String businessId, Map<String, Object> content, int priority) {
            return sendMessageWithPriorityAndRoutingKey(businessId, content, priority, configInfo.getRoutingKey());
        }
        
        /**
         * 发送业务消息（带优先级和指定路由键）
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @param priority   优先级（0-255，数字越大优先级越高）
         * @param routingKey 路由键
         * @return 是否发送成功
         */
        public boolean sendMessageWithPriorityAndRoutingKey(String businessId, Map<String, Object> content, int priority, String routingKey) {
            return sendMessageWithRoutingKey(businessId, content, routingKey, priority);
        }
        
        /**
         * 核心发送方法
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @param routingKey 路由键
         * @return 是否发送成功
         */
        private boolean sendMessageWithRoutingKey(String businessId, Map<String, Object> content, String routingKey) {
            return sendMessageWithRoutingKey(businessId, content, routingKey, 0);
        }
        
        /**
         * 核心发送方法（带优先级）
         *
         * @param businessId 业务ID
         * @param content    消息内容
         * @param routingKey 路由键
         * @param priority   优先级
         * @return 是否发送成功
         */
        private boolean sendMessageWithRoutingKey(String businessId, Map<String, Object> content, String routingKey, int priority) {
            try {
                String messageId = UUID.randomUUID().toString();

                // 构建消息体
                MessagePayload payload = new MessagePayload();
                payload.setId(messageId);
                payload.setType(configInfo.getMessageType());
                payload.setBusinessId(businessId);
                payload.setCreateTime(new Date());
                payload.setContent(content);
                payload.setRetryCount(0);
                payload.setMaxRetryCount(configInfo.getMaxRetryCount());

                // 构建消息属性
                MessageProperties properties = MessagePropertiesBuilder.newInstance()
                        .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                        .setMessageId(messageId)
                        .setTimestamp(new Date())
                        .setCorrelationId(messageId)
                        .setType(configInfo.getMessageType())
                        .build();
                
                // 设置优先级
                if (priority > 0) {
                    properties.setPriority(priority);
                }

                // 序列化消息内容
                byte[] bytes = objectMapper.writeValueAsBytes(payload);
                Message message = MessageBuilder.withBody(bytes)
                        .andProperties(properties)
                        .build();

                // 创建关联数据用于消息确认
                CorrelationData correlationData = new CorrelationData(messageId);
                
                // 发送消息
                rabbitTemplate.convertAndSend(
                        configInfo.getExchangeName(),
                        routingKey,
                        message,
                        correlationData
                );
                
                log.info("消息发送成功: messageId={}, type={}, businessId={}, exchange={}, routingKey={}",
                        messageId, configInfo.getMessageType(), businessId, 
                        configInfo.getExchangeName(), routingKey);
                
                return true;
            } catch (Exception e) {
                log.error("消息发送失败: type={}, businessId={}, error={}",
                        configInfo.getMessageType(), businessId, e.getMessage(), e);
                return false;
            }
        }
        
        /**
         * 发送延迟消息（使用配置的默认路由键）
         *
         * @param businessId  业务ID
         * @param content     消息内容
         * @param delayMillis 延迟时间（毫秒）
         * @return 是否发送成功
         */
        public boolean sendDelayMessage(String businessId, Map<String, Object> content, long delayMillis) {
            return sendDelayMessage(businessId, content, delayMillis, configInfo.getRoutingKey());
        }
        
        /**
         * 发送延迟消息（指定路由键）
         *
         * @param businessId  业务ID
         * @param content     消息内容
         * @param delayMillis 延迟时间（毫秒）
         * @param routingKey  路由键
         * @return 是否发送成功
         */
        public boolean sendDelayMessage(String businessId, Map<String, Object> content, long delayMillis, String routingKey) {
            try {
                String messageId = UUID.randomUUID().toString();

                // 构建消息体
                MessagePayload payload = new MessagePayload();
                payload.setId(messageId);
                payload.setType(configInfo.getMessageType());
                payload.setBusinessId(businessId);
                payload.setCreateTime(new Date());
                payload.setContent(content);
                payload.setRetryCount(0);
                payload.setMaxRetryCount(configInfo.getMaxRetryCount());

                // 构建消息属性
                MessageProperties properties = MessagePropertiesBuilder.newInstance()
                        .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                        .setMessageId(messageId)
                        .setTimestamp(new Date())
                        .setCorrelationId(messageId)
                        .setType(configInfo.getMessageType())
                        .setExpiration(String.valueOf(delayMillis))
                        .build();

                // 序列化消息内容
                byte[] bytes = objectMapper.writeValueAsBytes(payload);
                Message message = MessageBuilder.withBody(bytes)
                        .andProperties(properties)
                        .build();

                // 创建关联数据用于消息确认
                CorrelationData correlationData = new CorrelationData(messageId);
                
                // 发送消息到延迟队列
                rabbitTemplate.convertAndSend(
                        configInfo.getExchangeName(),
                        routingKey,
                        message,
                        correlationData
                );
                
                log.info("延迟消息发送成功: messageId={}, type={}, businessId={}, exchange={}, routingKey={}, delay={}ms",
                        messageId, configInfo.getMessageType(), businessId, 
                        configInfo.getExchangeName(), routingKey, delayMillis);
                
                return true;
            } catch (Exception e) {
                log.error("延迟消息发送失败: type={}, businessId={}, delay={}ms, error={}",
                        configInfo.getMessageType(), businessId, delayMillis, e.getMessage(), e);
                return false;
            }
        }
    }
}